using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using Microsoft.Ink;

namespace GraphicsPathTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            this.mPen = new Pen(Color.Black, 8.0f);
            this.mPen.StartCap = LineCap.Round;
            this.mPen.EndCap = LineCap.Round;
            this.mPen.DashCap = DashCap.Round;
            this.mStrokes = new List<Stroke>();
        }

        private Graphics CG(Graphics graphics)
        {
            graphics.SmoothingMode = SmoothingMode.AntiAlias;
            return graphics;
        }
        
        private void panel1_MouseDown(object sender, MouseEventArgs e)
        {
            if (this.checkBox1.Checked)
            {
                Rectangle rect = new Rectangle(e.X, e.Y, 1, 1);
                foreach (Stroke stroke in this.mStrokes)
                {
                    if (/*(rect.IntersectsWith(stroke.BoundingBox)) &&*/ (stroke.HitTest(e.Location)))
                    {
                        stroke.Color = Color.Red;
                        this.panel1.Invalidate(stroke.BoundingBox);
                        this.mCurrentStroke = stroke;
                        break;
                    }
                }
            }
            else
            {
                this.mCurrentStroke = new Stroke(this.mPen.Color, this.mPen.Width);
                this.mCurrentStroke.AddPoint(e.Location);
            }
        }

        private void panel1_MouseMove(object sender, MouseEventArgs e)
        {
            if (this.checkBox1.Checked)
            {
                return;
            }

            if (e.Button == MouseButtons.Left)
            {
                using (Graphics graphics = CG(this.panel1.CreateGraphics()))
                {
                    graphics.DrawLine(this.mPen, this.mCurrentStroke.LastPoint, e.Location);
                }
                
                this.mCurrentStroke.AddPoint(e.Location);
            }
        }

        private void panel1_MouseUp(object sender, MouseEventArgs e)
        {
            if (this.checkBox1.Checked)
            {
                return;
            }

            if (e.Location == this.mCurrentStroke.LastPoint)
            {
                using (Graphics graphics = CG(this.panel1.CreateGraphics()))
                {
                    graphics.DrawLine(this.mPen, this.mCurrentStroke.LastPoint.X + 0.0f, this.mCurrentStroke.LastPoint.Y + 0.0f, e.X + 0.001f, e.Y + 0.001f);
                }
            }
            this.mCurrentStroke.ComputeBoundingBox();
            this.mStrokes.Add(this.mCurrentStroke);
        }

        private void panel1_Paint(object sender, PaintEventArgs e)
        {
            Graphics graphics = CG(e.Graphics);
            //graphics.Clear(Color.White);
            graphics.SetClip(e.ClipRectangle);
            foreach (Stroke stroke in this.mStrokes)
            {
                stroke.Draw(graphics);
            }
        }

        private Pen mPen;
        private List<Stroke> mStrokes;
        private Stroke mCurrentStroke;
    }

    public class Stroke
    {
        private Rectangle mBoundingBox;
        private GraphicsPath mPath;
        private Point mLastPoint;
        private Pen mPen;

        public Stroke(Color pColor, float pThickness)
        {
            this.mPath = new GraphicsPath();
            this.mPen = new Pen(pColor, pThickness);
            this.mPen.StartCap = LineCap.Round;
            this.mPen.EndCap = LineCap.Round;
            this.mPen.DashCap = DashCap.Round;
        }

        public Point LastPoint
        {
            get { return this.mLastPoint; }
        }

        public Color Color
        {
            get
            {
                return this.mPen.Color;
            }
            set
            {
                this.mPen.Color = value;
            }
        }

        public Rectangle BoundingBox
        {
            get
            {
                return this.mBoundingBox;
            }
        }

        public void AddPoint(Point point)
        {
            if (this.mLastPoint.IsEmpty)
            {
                // add the line
                this.mPath.AddLine(point, point);
            }
            else
            {
                // add the line
                this.mPath.AddLine(this.mLastPoint, point);
            }
            this.mLastPoint = point;
        }

        public void Draw(Graphics pGraphics)
        {
            //pGraphics.DrawPath(pPen, this.mPath);
            if (this.mPath.PointCount == 2)
            {
                pGraphics.DrawLine(this.mPen, this.mLastPoint.X, this.mLastPoint.Y, this.mLastPoint.X + 0.001f, this.mLastPoint.Y + 0.001f);
            }
            else
            {
                for (int i = 2; i < this.mPath.PointCount; i++)
                {
                    pGraphics.DrawLine(this.mPen, this.mPath.PathPoints[i - 1], this.mPath.PathPoints[i]);
                }
            }
            if (this.mPen.Color == Color.Red)
            {
                Pen pen = new Pen(Color.Blue, 1.0f);
                pGraphics.DrawRectangle(pen, this.mBoundingBox);
            }
        }

        public bool HitTest(Point pPoint)
        {
            bool isOutlineVisible = this.mPath.IsOutlineVisible(pPoint, this.mPen);
            
            return (isOutlineVisible);
        }

        public void ComputeBoundingBox()
        {
            int penThickness = (int)Math.Ceiling((double)this.mPen.Width);
            float left, right, top, bottom;
            left = this.mLastPoint.X;
            right = this.mLastPoint.X;
            top = this.mLastPoint.Y;
            bottom = this.mLastPoint.Y;

            foreach (PointF point in this.mPath.PathPoints)
            {
                left = Math.Min(left, point.X);
                right = Math.Max(right, point.X);
                top = Math.Min(top, point.Y);
                bottom = Math.Max(bottom, point.Y);
            }

            this.mBoundingBox.X = (int)left;
            this.mBoundingBox.Y = (int)top;
            this.mBoundingBox.Width = (int)(right - left);
            this.mBoundingBox.Height = (int)(bottom - top);

            penThickness = penThickness / 2 + 1;
            this.mBoundingBox.Inflate(penThickness, penThickness);
        }
    }
}